#include "preHeader.h"
#include "CharacterMgr.h"
#include "AccountMgr.h"
#include "TeleportMgr.h"
#include "Team/TeamMgr.h"
#include "WordFilter/WordFilter.h"
#include "Session/MapServerMgr.h"
#include "Session/SocialServerSession.h"
#include "Session/DBPSession.h"
#include "Session/DBPHelper.h"
#include "Manager/DataMgr.h"
#include "GameServer.h"

CharacterMgr::CharacterMgr()
: m_playerLevelMax(0)
{
}

CharacterMgr::~CharacterMgr()
{
	for (const auto& pair : m_CharacterInfoMap) {
		delete pair.second;
	}
}

GErrorCode CharacterMgr::CheckPlayerName(const std::string_view& name)
{
	if (name.size() < 4) {
		return CharNameTooShort;
	}
	if (name.size() > 64) {
		return CharNameTooLong;
	}
	if (name.find_first_of("\x20\t\r\n") != std::string_view::npos) {
		return CharNameInvalid;
	}
	if (sWordFilter.IsWordFilter(name)) {
		return CharNameIllegality;
	}
	return CommonSuccess;
}

GErrorCode CharacterMgr::CheckGuildName(const std::string_view& name)
{
	if (name.size() < 4) {
		return CharNameTooShort;
	}
	if (name.size() > 64) {
		return CharNameTooLong;
	}
	if (name.find_first_of("\x20\t\r\n") != std::string_view::npos) {
		return CharNameInvalid;
	}
	if (sWordFilter.IsWordFilter(name)) {
		return CharNameIllegality;
	}
	return CommonSuccess;
}

bool CharacterMgr::LoadDataFromDB()
{
	NetPacket rpcReqPck(CDBP_LOAD_ALL_PLAYER_CHARACTER_INFO);
	auto errCode = DBPHelper::RPCBlockInvoke(&sDBPSession, rpcReqPck,
		[=](INetStream& pck) {
		while (!pck.IsReadableEmpty()) {
			InstPlayerOutlineInfo instInfo;
			LoadFromINetStream(instInfo, pck);
			auto errCode = UpdateCharacter(instInfo);
			if (errCode != CommonSuccess) {
				WLOG("Load Character Data [%u] Error(%d).",
					instInfo.ipcInstID, errCode);
			}
		}
	}, sDBPSessionUpdate4RPCBlockInvoke);
	if (errCode != RPCErrorNone) {
		return false;
	}
	rpcReqPck.Reset(CDBP_LOAD_ALL_SOCIAL_FRIEND);
	errCode = DBPHelper::RPCBlockInvoke(&sDBPSession, rpcReqPck,
		[=](INetStream& pck) {
		while (!pck.IsReadableEmpty()) {
			social_friend instInfo;
			LoadFromINetStream(instInfo, pck);
			auto pChar = GetCharacter(instInfo.characterId);
			if (pChar != NULL) {
				pChar->socialData.LoadFriendFromDB(std::move(instInfo));
			}
		}
	}, sDBPSessionUpdate4RPCBlockInvoke);
	if (errCode != RPCErrorNone) {
		return false;
	}
	rpcReqPck.Reset(CDBP_LOAD_ALL_SOCIAL_IGNORE);
	errCode = DBPHelper::RPCBlockInvoke(&sDBPSession, rpcReqPck,
		[=](INetStream& pck) {
		while (!pck.IsReadableEmpty()) {
			social_ignore instInfo;
			LoadFromINetStream(instInfo, pck);
			auto pChar = GetCharacter(instInfo.characterId);
			if (pChar != NULL) {
				pChar->socialData.LoadIgnoreFromDB(std::move(instInfo));
			}
		}
	}, sDBPSessionUpdate4RPCBlockInvoke);
	if (errCode != RPCErrorNone) {
		return false;
	}
	return true;
}

GErrorCode CharacterMgr::UpdateCharacter(const InstPlayerOutlineInfo& info)
{
	bool isNewChar = false;
	auto playerGuid = GetGuidFromLowGuid4Player(info.ipcInstID);
	auto itr = m_CharacterInfoMap.find(playerGuid);
	if (itr == m_CharacterInfoMap.end()) {
		itr = m_CharacterInfoMap.emplace(playerGuid,
			new Character(playerGuid, info.ipcAcctID, info.ipcServerID)).first;
	}

	Character* pChar = itr->second;
	if (CheckCharacter(pChar) != CommonSuccess) {
		m_CharacterNameMap.emplace(info.ipcNickName, pChar);
		pChar->name = info.ipcNickName;
		isNewChar = true;
		BIT_CLR(pChar->serverFlags, Character::eDeleted);
		BIT_CLR(pChar->serverFlags, Character::eUninitialized);
	}

	pChar->career = info.ipcCareer;
	pChar->gender = info.ipcGender;
	pChar->lastLevel = info.ipcLevel;
	pChar->lastVipLevel = info.ipcVipLevel;
	pChar->lastInstGuid = MakeInstGuid(info.ipcMapType, info.ipcMapID);
	pChar->lastPos = {info.ipcPosX, info.ipcPosY, info.ipcPosZ, info.ipcPosO};
	pChar->lastOnlineTime = info.ipcLastOnlineTime;
	pChar->charFlags = info.ipcFlags;
	pChar->rankVals = info.ipcRankValue;
	pChar->gsReadVals = info.ipcGsReadValue;

	UpdatePlayerLevelMax(pChar->lastLevel);

	if (sGameServer.IsServing()) {
		if (isNewChar) {
			NewCharacterRankData(pChar);
		} else {
			UpdateCharacterRankData(pChar);
		}
	}

	return CommonSuccess;
}

GErrorCode CharacterMgr::RemoveCharacter(Character* pChar)
{
	BIT_SET(pChar->serverFlags, Character::eDeleted);
	m_CharacterNameMap.erase(pChar->name);

	RemoveCharacterRankData(pChar);
	pChar->socialData.OnRemoveCharacter();

	return CommonSuccess;
}

Character* CharacterMgr::GetCharacter(uint32 uid) const
{
	return GetCharacter(GetGuidFromLowGuid4Player(uid));
}

Character* CharacterMgr::GetCharacter(ObjGUID guid) const
{
	if (guid == ObjGUID_NULL) {
		return NULL;
	}

	auto itr = m_CharacterInfoMap.find((guid.SID = 0, guid));
	if (itr == m_CharacterInfoMap.end()) {
		return NULL;
	}

	Character* pChar = itr->second;
	if (CheckCharacter(pChar) != CommonSuccess) {
		return NULL;
	}

	return pChar;
}

Character* CharacterMgr::GetCharacterByName(const std::string& name) const
{
	auto itr = m_CharacterNameMap.find(name);
	if (itr == m_CharacterNameMap.end()) {
		return NULL;
	}

	Character* pChar = itr->second;
	if (CheckCharacter(pChar) != CommonSuccess) {
		return NULL;
	}

	return pChar;
}

GErrorCode CharacterMgr::CheckCharacter(Character* pChar)
{
	if (pChar == NULL) {
		return CharInvalid;
	}
	if (BIT_ISSET(pChar->serverFlags, Character::eDeleted)) {
		return CharNotExist;
	}
	if (BIT_ISSET(pChar->serverFlags, Character::eInMigration)) {
		return CharInMigration;
	}
	if (BIT_ISSET(pChar->serverFlags, Character::eUninitialized)) {
		return CharIsUninitialized;
	}
	return CommonSuccess;
}

GErrorCode CharacterMgr::LoginCharacter(Account* pAccount, Character* pChar)
{
	DBGASSERT(!pAccount->IsCharacterOnline() && !pChar->IsAccountOnline());
	if (pAccount->IsCharacterOnline() || pChar->IsAccountOnline()) {
		return CommonInternalError;
	}

	pAccount->SetCharacter(pChar);
	pChar->SetAccount(pAccount);
	pChar->isOnline = true;
	OnCharacterOnline(pChar);

	sTeleportMgr.HandleLoginRequest(pChar->guid,
		pChar->lastInstOwner, pChar->lastInstGuid, pChar->lastPos,
		TeleportType::Login);

	return CommonSuccess;
}

GErrorCode CharacterMgr::LogoutCharacter(Account* pAccount, Character* pChar)
{
	DBGASSERT(pAccount->IsCharacterOnline() && pChar->IsAccountOnline());
	if (!pAccount->IsCharacterOnline() || !pChar->IsAccountOnline()) {
		WLOG("LogoutCharacter[%u->%u] status error code 1, %d <--> %d.",
			pAccount->uid, pChar->guid.UID,
			pAccount->IsCharacterOnline(), pChar->IsAccountOnline());
	}
	DBGASSERT(pAccount->GetCharacter() == pChar);
	if (pAccount->GetCharacter() != pChar) {
		WLOG("LogoutCharacter[%u->%u] status error code 2, %p <--> %p.",
			pAccount->uid, pChar->guid.UID, pAccount->GetCharacter(), pChar);
	}
	DBGASSERT(pChar->GetAccount() == pAccount);
	if (pChar->GetAccount() != pAccount) {
		WLOG("LogoutCharacter[%u->%u] status error code 3, %p <--> %p.",
			pAccount->uid, pChar->guid.UID, pChar->GetAccount(), pAccount);
	}
	NLOG("client account`%u` logout character[%u,%s].",
		pAccount->uid, pChar->guid.UID, pChar->name.c_str());

	pChar->isOnline = false;
	pChar->lastOnlineTime = GET_UNIX_TIME;
	pChar->SetAccount(NULL);
	pAccount->SetCharacter(NULL);
	OnCharacterOffline(pChar);

	auto pMapServer = sMapServerMgr.GetMapServer(pChar->lastInstGuid);
	if (pMapServer != NULL) {
		pMapServer->RemoveCharacter(pChar);
	}

	sTeleportMgr.TeleportRequestFailed(
		pChar->guid, LOGIN_ERROR_CHARACTER_OFFLINE);
	sTeleportMgr.PlayerLoadRequestFailed(
		pChar->guid, LOGIN_ERROR_CHARACTER_OFFLINE);

	return CommonSuccess;
}

void CharacterMgr::OnCharacterOnline(Character* pChar)
{
	m_OnlineCharacters.emplace(pChar->guid, pChar);
	SendOperating4Login(pChar);
	SendOperating4EventActvt(pChar, OperatingEventType::Online, {});
	sSocialServerSession.PushCharacterOnline(pChar);
	sTeamMgr.OnCharacterOnline(pChar);
}

void CharacterMgr::OnCharacterOffline(Character* pChar)
{
	sTeamMgr.OnCharacterOffline(pChar);
	sSocialServerSession.PushCharacterOffline(pChar);
	SendOperating4EventActvt(pChar, OperatingEventType::Offline, {});
	m_OnlineCharacters.erase(pChar->guid);
}

void CharacterMgr::DisposeMapServerOffline(MapServerProxy* pMapServer)
{
	for (auto itr = m_OnlineCharacters.begin(); itr != m_OnlineCharacters.end();) {
		auto&[playerGuid, pChar] = *itr++;
		auto pMapServerNow = sMapServerMgr.GetMapServer(pChar->lastInstGuid);
		if (pMapServerNow == pMapServer) {
			sAccountMgr.KickAccount(pChar->acct, LOGIN_ERROR_MAP_SERVER_OFFLINE);
			continue;
		}
	}
}

void CharacterMgr::InitPlayerInstance(inst_player_char& instInfo)
{
	instInfo.ipcMapType = sGameConfig.BornMapType;
	instInfo.ipcMapID = sGameConfig.BornMapID;
	instInfo.ipcPosX = sGameConfig.BornPos.x;
	instInfo.ipcPosY = sGameConfig.BornPos.y;
	instInfo.ipcPosZ = sGameConfig.BornPos.z;
	instInfo.ipcPosO = sGameConfig.BornPos.o;

	instInfo.ipcLevel = 1;
	instInfo.ipcCurHP = 100;
	instInfo.ipcCurMP = 100;

	instInfo.ipcCreateTime = GET_UNIX_TIME;
	instInfo.ipcJsonValue = "null";
}

void CharacterMgr::OnCreatePlayerInstance(uint32 uid, INetStream& pck, int32 err)
{
	auto pAccount = sAccountMgr.GetAccount(uid);
	if (err != RPCErrorNone) {
		WLOG("OnCreatePlayerInstance[%u] failed, %d.", uid, err);
		if (pAccount != NULL) {
			pAccount->SendRespError(
				SMSG_CHARACTER_CREATE_RESP, CommonInternalError);
		}
		return;
	}
	if (pAccount == NULL) {
		return;
	}

	uint32 charId;
	pck >> charId;
	NetPacket resp(SMSG_CHARACTER_CREATE_RESP);
	resp << CommonSuccess << charId;
	pAccount->SendPacket(resp);

	NLOG("client account`%u` create character`%u` successfully.", uid, charId);
}

void CharacterMgr::OnDeletePlayerInstance(uint32 uid, INetStream& pck, int32 err)
{
	auto pAccount = sAccountMgr.GetAccount(uid);
	if (err != RPCErrorNone) {
		WLOG("OnDeletePlayerInstance[%u] failed, %d.", uid, err);
		if (pAccount != NULL) {
			pAccount->SendRespError(
				SMSG_CHARACTER_DELETE_RESP, CommonInternalError);
		}
		return;
	}
	if (pAccount == NULL) {
		return;
	}

	uint32 playerId;
	pck >> playerId;
	auto pChar = sCharacterMgr.GetCharacter(playerId);
	if (pChar != NULL) {
		sCharacterMgr.RemoveCharacter(pChar);
	}

	uint32 nChange;
	pck >> nChange;
	NetPacket resp(SMSG_CHARACTER_DELETE_RESP);
	resp << CommonSuccess << playerId << nChange;
	pAccount->SendPacket(resp);

	NLOG("client account`%u` delete character`%u` successfully.", uid, playerId);
}

void CharacterMgr::PushAllCharacterRankDatas()
{
	MaxNetPacket pack(CGX_RANK_NEW_PLAYER);
	int i = 0;
	for (auto&[guid, pChar] : m_CharacterInfoMap) {
		if (CheckCharacter(pChar) != CommonSuccess) {
			continue;
		}
		pack << pChar->guid.UID;
		for (int type = 0; type < (int)RANK_SUBTYPE::PLAYER_COUNT; ++type) {
			PackCharacterRankData(pack, pChar, RANK_SUBTYPE(type));
		}
		if (++i >= 2500) {
			sSocialServerSession.PushSendPacket(pack);
			pack.Clear();
			i = 0;
		}
	}
	if (i > 0) {
		sSocialServerSession.PushSendPacket(pack);
	}
}

void CharacterMgr::NewCharacterRankData(Character* pChar)
{
	NetPacket pack(CGX_RANK_NEW_PLAYER);
	pack << pChar->guid.UID;
	for (int type = 0; type < (int)RANK_SUBTYPE::PLAYER_COUNT; ++type) {
		PackCharacterRankData(pack, pChar, RANK_SUBTYPE(type));
	}
	sSocialServerSession.PushSendPacket(pack);
}

void CharacterMgr::UpdateCharacterRankData(Character* pChar, int subType)
{
	NetPacket pack(CGX_RANK_UPDATE_PLAYER);
	if (subType == -1) {
		for (int type = 0; type < (int)RANK_SUBTYPE::PLAYER_COUNT; ++type) {
			PackCharacterRankData(pack, pChar, RANK_SUBTYPE(type),
				(int)RankDataFlag::UID | (int)RankDataFlag::TYPE);
		}
	} else if (subType >= 0 && subType < (int)RANK_SUBTYPE::PLAYER_COUNT) {
		PackCharacterRankData(pack, pChar, RANK_SUBTYPE(subType),
			(int)RankDataFlag::UID | (int)RankDataFlag::TYPE);
	}
	sSocialServerSession.PushSendPacket(pack);
}

void CharacterMgr::RemoveCharacterRankData(Character* pChar)
{
	NetPacket pack(CGX_RANK_REMOVE_PLAYER);
	pack << pChar->guid.UID;
	sSocialServerSession.PushSendPacket(pack);
}

void CharacterMgr::PackCharacterRankData(INetPacket& pck,
	Character* pChar, RANK_SUBTYPE subType, int flags)
{
	if ((flags & (int)RankDataFlag::UID) != 0) {
		pck << pChar->guid.UID;
	}
	if ((flags & (int)RankDataFlag::TYPE) != 0) {
		pck << (uint8)subType;
	}
	switch (subType) {
	case RANK_SUBTYPE::PLAYER_LEVEL:
		pck << pChar->lastLevel << pChar->rankVals.lastLevelTime;
		break;
	case RANK_SUBTYPE::PLAYER_FIGHT_VALUE:
		pck << pChar->gsReadVals.lastFightValue << pChar->rankVals.lastFightValueTime;
		break;
	}
}

void CharacterMgr::SendOperating4Login(Character* pChar)
{
	int i = (int)OperatingCareType::Login;
	if (sDataMgr.isOperatingCare(i)) {
		NetPacket pack2SS(CGX_OPERATING_LOGIN);
		SendOperatingPacket2SS(pChar, pack2SS);
	}
}

void CharacterMgr::SendOperating4EventActvt(
	Character* pChar, OperatingEventType eventType, params<uint32> params)
{
	int i = (int)OperatingCareType::BeginEventActvt + (int)eventType;
	if (sDataMgr.isOperatingCare(i)) {
		NetPacket pack2SS(CGX_OPERATING_EVENT_ACTVT);
		pack2SS << (uint32)eventType;
		for (auto param : params) {
			pack2SS << param;
		}
		SendOperatingPacket2SS(pChar, pack2SS);
	}
}

void CharacterMgr::SendOperatingPacket2SS(Character* pChar, const INetPacket& pck)
{
	std::string info = PackOperatingPlayerInfo(pChar);
	NetPacket wrapper(pck.GetOpcode());
	const INetPacket *pcks[] = {&wrapper};
	const std::string_view datas[] = {info, pck.CastReadableStringView()};
	sSocialServerSession.
		PushSendPacket(pcks, ARRAY_SIZE(pcks), datas, ARRAY_SIZE(datas));
}

std::string CharacterMgr::PackOperatingPlayerInfo(Character* pChar)
{
	NetBuffer buffer;
	buffer << pChar->guid.UID
		<< pChar->lastLevel << pChar->lastVipLevel;
	return buffer.CastBufferString();
}

void CharacterMgr::UpdatePlayerLevelMax(uint32 playerLevel)
{
	if (m_playerLevelMax < playerLevel) {
		m_playerLevelMax = playerLevel;
		if (sGameServer.IsServing()) {
			SyncPlayerLevelMax2MapServer();
		}
	}
}

void CharacterMgr::SyncPlayerLevelMax2MapServer(uint32 serviceId) const
{
	NetPacket pack(GS_SYNC_PLAYER_LEVEL_MAX);
	pack << m_playerLevelMax;
	if (serviceId != -1) {
		sMapServerMgr.SendPacket2MapServer(serviceId, pack);
	} else {
		sMapServerMgr.BroadcastPacket2AllMapServer(pack);
	}
}
